# -*- coding: utf-8 -*-
from gevent import monkey

monkey.patch_all(Event=True, subprocess=True)
from gevent.server import StreamServer
from multiprocessing import Process
from gevent import Greenlet
from gevent import socket
from gevent import queue as Queue
import logging
from logging.handlers import RotatingFileHandler
import sys

sys.path.append('..')
from handle import DeviceConnect, dispatch
from core.packet import Read
from core.model import Loger
from agent.client import Dispose
from agent.alphabet import NO
from core.tools import FamilyPush
import setting


def interact(imei, instruct, params):
    """
    conn.interact 返回 OK, NO, TIMEOUT,返回成功、失败
    """
    try:
        conn = DeviceDict[imei]
    except KeyError:
        return NO
    result = conn.interact(instruct, params)
    return result


def sendtodev(imei, instruct, params):
    """
    返回值忽略
    """
    try:
        conn = DeviceDict[imei]
    except KeyError:
        return None
    conn.send(instruct, params)
    return None


islinux = True if 'linux' in sys.platform else False


def devicehandle(sock, address):
    try:
        sock.settimeout(setting.server['timeout'])
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
        if islinux:
            sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 300)
            sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPCNT, 3)
            sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 15)
        imei = ''
        version, identify, timezone, imei, instruct, params = Read(sock)
        if instruct in dispatch:
            # 解析设备所发送指令
            dc = DeviceConnect(sock, address, version, identify, imei)
            # logger.info('imei:%s upon %r' % (imei, address))
            Loger(model='server', ident=imei, event='status', value='upon %r' % (address,)).save()
            dc.handle(identify, timezone, imei, instruct, params)
            # 处理设备发过来的第一个指令
            taskQueue.put((1, imei, address, dc))
            dc.start()
            # logger.info('imei:%s down %r' % (imei, address))
            Loger(model='server', ident=imei, event='status', value="down %r" % (address,)).save()
    except socket.error:
        pass
    except:
        logger.error(imei + ' ' + repr(address), exc_info=True)
    finally:
        if imei:
            taskQueue.put((0, imei, address, ''))


DeviceDict = {}
DeviceStat = {}
taskQueue = Queue.Queue()


def handle_devicedict():
    while 1:
        flag, imei, address, dc = taskQueue.get()
        try:
            if flag:
                try:
                    stale_dc = DeviceDict[imei]
                    stale_dc.stop()
                    # logger.warning('%s already in DeviceDict' % imei)
                except KeyError:
                    pass
                except socket.error:
                    logger.error('ERROR in stop stale conn', exc_info=True)
                finally:
                    DeviceDict[imei] = dc
                    DeviceStat[imei] = address
                    if dc.model.family:
                        FamilyPush(dc.model.family, {'type': 'babystatus', 'imei': imei, 'status': 'online'})
            else:
                try:
                    if address == DeviceStat[imei]:
                        try:
                            off_dc = DeviceDict.pop(imei)
                            if off_dc.model:
                                FamilyPush(off_dc.model.family,
                                           {'type': 'babystatus', 'imei': imei, 'status': 'offline'})
                            off_dc.stop()
                        except KeyError:
                            logger.warning('%s nofind in DeviceDict' % imei)
                except KeyError:
                    logger.warning('%s already offline' % imei)
        except:
            logger.error('ERROR in HandleDiction', exc_info=True)


logger = logging.getLogger('server')
logfile = setting.server['logfile']
File_logging = RotatingFileHandler(logfile, maxBytes=10 * 1024 * 1024, backupCount=50)
File_logging.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
File_logging.setLevel(setting.server['loglevel'])
logger.addHandler(File_logging)
logger.setLevel(setting.server['loglevel'])
if setting.server['visible']:
    printlog = logging.StreamHandler()
    printlog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(filename)s(%(lineno)s):%(message)s'))
    printlog.setLevel(setting.server['loglevel'])
    logger.addHandler(printlog)

if __name__ == '__main__':
    server = StreamServer(('0.0.0.0', setting.server['port']), devicehandle, backlog=100000)
    server.init_socket()
    server.max_accept = 1


    def serve_forever():
        Greenlet.spawn(handle_devicedict).start()
        dispose = Dispose(DeviceDict, interact=interact, interact_noblock=sendtodev)
        dispose.start()
        try:
            server.start()
            try:
                server._stop_event.wait()
            except:
                logger.error('Dangerous!', exc_info=True)
                raise
        except KeyboardInterrupt:
            pass


    # SIGCHLD
    for i in xrange(setting.server['worker'] - 1):
        Process(target=serve_forever, args=tuple()).start()
    serve_forever()
